package org.sdnstproject.interlayer.interlayerProxy;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.Socket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Agent implements Runnable{
	private static final Logger log = LoggerFactory.getLogger(Agent.class);
	private volatile BufferedReader in;
	private volatile BufferedWriter out;
	private Process proc = null;
	private int procPID;
	private BufferedReader stderrBr;
    private FileWriter output;
	private Configuration config = Configuration.getInstance();
	private boolean isController;
	private String logName;
	private String[] cmd;
	
	public Agent(boolean isController) {
		this.isController = isController;
		if(isController) {
			this.logName = "fuzzingController.log";
			this.cmd = new String[] {"ssh",config.getControllerSSH(),"python","controllerAgent.py"};
		} else {
			this.logName = "fuzzingNetwork.log";
			this.cmd = new String[] {"ssh",config.getSwitchSSH(),"sudo","python","switchAgent.py"};
		}
	}
	public void setIO(BufferedReader inReader, BufferedWriter ouWriter) {
		this.in = inReader;
		this.out = ouWriter;
	}
	
	@Override
	public void run() {
		
		try {
			ProcessBuilder pb = new ProcessBuilder(cmd);
			pb.redirectErrorStream(true);
            proc = pb.start();
            while(out == null) {
//            	log.info("waiting for agent connected.");
            	Thread.sleep(500);
            }
            
            stderrBr = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            checkLogDirectory();
            //第二个参数：是否是append
            String logPath = config.getInterlayer_ROOT() + "log/" + logName;
            output = new FileWriter(logPath, false);
		  	String line;
	        output.write("=================================================================================\n");
	        while ((line = stderrBr.readLine()) != null) {
	        	output.append(line + "\n");
	        	output.flush();
	        }
            Field pidField = Class.forName("java.lang.UNIXProcess").getDeclaredField("pid");
            pidField.setAccessible(true);
            Object value = pidField.get(proc);
            procPID = (Integer) value;
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(in != null) in.close();
				if(out != null) out.close();
				stderrBr.close();
				output.close();
				if (procPID != -1) {
					shutSelfDown();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public void checkLogDirectory() {
        File dir = new File(config.getInterlayer_ROOT() + "log/");
        if (!dir.exists()) {
            dir.mkdirs();
        }
    }
	
	public boolean doControl(String command) {
		if(out == null) {
			log.info("out is null.");
			return false;
		}
		try {
			out.write(command);
			out.flush();
//			if(in.readLine().contains("OK")) return true;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public boolean isStartApp() {
		if(in == null) {
			log.info("in is null.");
			return false;
		}
		try {
			if(in.readLine().equals("app has start")) return true;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}
	public void stopSelf() {
		if(out == null) {
			shutSelfDown();
		}else {
			try {
				out.write("stop agent");
			} catch (IOException e) {
				e.printStackTrace();
				shutSelfDown();
			}
		}
		
	}
	public void shutSelfDown() {
		try {
			if(isController)
				Runtime.getRuntime().exec("ssh " + config.getControllerSSH() + " sudo pkill java && sudo pkill ryu-manger");
			else
				Runtime.getRuntime().exec("ssh " + config.getSwitchSSH() + " sudo mn -c && sudo killall python");
			proc = Runtime.getRuntime().exec("sudo kill -9 " + this.procPID);
			proc.waitFor();
			procPID = -1;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public BufferedReader getIn() {
		return in;
	}

}
